/*
 * Unidata Platform
 * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 *
 * Commercial License
 * This version of Unidata Platform is licensed commercially and is the appropriate option for the vast majority of use cases.
 *
 * Please see the Unidata Licensing page at: https://unidata-platform.com/license/
 * For clarification or additional options, please contact: info@unidata-platform.com
 * -------
 * Disclaimer:
 * -------
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 */
package org.unidata.mdm.rest.search.service;

import java.lang.reflect.Array;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.apache.cxf.jaxrs.ext.xml.ElementClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.unidata.mdm.rest.search.configuration.SearchRestConfigurationConstants;
import org.unidata.mdm.rest.search.ro.SearchComplexRO;
import org.unidata.mdm.rest.search.ro.SearchResultRO;
import org.unidata.mdm.rest.search.type.rendering.SearchRestInputRenderingAction;
import org.unidata.mdm.rest.search.type.rendering.SearchRestOutputRenderingAction;
import org.unidata.mdm.rest.system.ro.ErrorResponse;
import org.unidata.mdm.rest.system.service.AbstractRestService;
import org.unidata.mdm.search.context.ComplexSearchRequestContext;
import org.unidata.mdm.search.context.ComplexSearchRequestContext.ComplexSearchRequestContextBuilder;
import org.unidata.mdm.search.context.SearchRequestContext;
import org.unidata.mdm.search.context.SearchRequestContext.SearchRequestContextBuilder;
import org.unidata.mdm.search.dto.SearchResultDTO;
import org.unidata.mdm.search.service.SearchService;
import org.unidata.mdm.system.dto.OutputContainer;
import org.unidata.mdm.system.service.RenderingService;
import org.unidata.mdm.system.type.runtime.MeasurementContextName;
import org.unidata.mdm.system.type.runtime.MeasurementPoint;

/**
 * @author Mikhail Mikhailov
 *         Search REST service.
 */
@Path("/")
@Consumes({"application/json"})
@Produces({"application/json"})
public class SearchRestService extends AbstractRestService {
    /**
     * Logger.
     */
    public static final Logger LOGGER = LoggerFactory.getLogger(SearchRestService.class);
    /**
     * Search service.
     */
    @Autowired
    private SearchService searchService;
    /**
     * Rendering support.
     */
    @Autowired
    private RenderingService renderingService;

    /**
     * Search form request
     * @param input the form
     * @return result
     */
    @POST
    @ElementClass(response = SearchResultRO.class)
    @Operation(
        description = "Общий унифицированный поисковый запрос.",
        method = "POST",
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = SearchResultRO.class)), responseCode = "200"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = ErrorResponse.class)), responseCode = "500")
        }
    )
    public Response search(@Parameter(description = "Поисковый запрос") SearchComplexRO input) {

        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_SEARCH_SIMPLE);
        MeasurementPoint.start();

        try {

            ComplexSearchRequestContextBuilder collector = ComplexSearchRequestContext.builder();
            renderingService.renderInput(SearchRestInputRenderingAction.COMPLEX_SEARCH_INPUT, collector, input);

            OutputContainer container = searchService.search(collector.build());

            SearchResultRO output = new SearchResultRO();
            renderingService.renderOutput(SearchRestOutputRenderingAction.COMPLEX_SEARCH_OUTPUT, container, output);

            return ok(output);
        } finally {
            MeasurementPoint.stop();
        }
    }

    /**
     * Search form request
     * @param input the form
     * @return result
     */
    @POST
    @Path("/" + SearchRestConfigurationConstants.SEARCH_PATH_COMPLEX)
    @ElementClass(response = SearchResultRO.class)
    @Operation(
        description = "Сложный поиск через связи.",
        method = "POST",
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = SearchResultRO.class)), responseCode = "200"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = ErrorResponse.class)), responseCode = "500")
        }
    )
    public Response complexSearch(@Parameter(description = "Поисковый запрос") SearchComplexRO input) {

        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_SEARCH_COMPLEX);
        MeasurementPoint.start();

        try {

            ComplexSearchRequestContextBuilder collector = ComplexSearchRequestContext.builder();
            renderingService.renderInput(SearchRestInputRenderingAction.COMPLEX_SEARCH_INPUT, collector, input);

            OutputContainer container = searchService.search(collector.build());

            SearchResultRO output = new SearchResultRO();
            renderingService.renderOutput(SearchRestOutputRenderingAction.COMPLEX_SEARCH_OUTPUT, container, output);

            return ok(output);
        } finally {
            MeasurementPoint.stop();
        }
    }

    /**
     * Simple 'search-as-you-type'.
     * @param input simple search request
     * @return array of strings
     */
    @POST
    @Path("/" + SearchRestConfigurationConstants.SEARCH_PATH_SAYT)
    @Operation(
        description = "Найти записи, содержащие в заданных полях текст непосредственно во время ввода.",
        method = "POST",
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = Array.class)), responseCode = "200"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = ErrorResponse.class)), responseCode = "500")
        }
    )
    public Response saytSearch(@Parameter(description = "Поисковый запрос") SearchComplexRO input) {
        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_SEARCH_SAYT);
        MeasurementPoint.start();
        try {

            input.setSayt(true);

            SearchRequestContextBuilder collector = SearchRequestContext.builder(input.getEntity());
            renderingService.renderInput(SearchRestInputRenderingAction.SAYT_SEARCH_INPUT, collector, input);

            SearchResultDTO container = searchService.search(collector.build());

            SearchResultRO output = new SearchResultRO();
            renderingService.renderOutput(SearchRestOutputRenderingAction.SAYT_SEARCH_OUTPUT, container, output);

            return ok(output);
        } finally {
            MeasurementPoint.stop();
        }
    }
}
